package
{
	import flash.display.*;
	import flash.events.*;
	import flash.geom.Point;
	
	public class RightTool extends CursorTool
	{
		// constants
		public const MAGNET_WIDTH:int = 6;
		public const MAGNET_HEIGHT:int = 50;
		public const JOINT_DIAMETER:int = 8;
		public const MAX_WIGGLE_PROP:Number = .05; // the percent of total wiggle that may be conducted in a single frame
		// public vars
		public var marginOfError_snap:Number, marginOfError_degrees:Number; // Number of pixels to snap to angle, Number of degrees in which parallel lines will snap
		private var _state:String;
		private var _outOfBounds:Boolean = false;
		
		// private objects
		private var drawAngles:Array, oobshapes:Array;
		private var overAngle:DrawAngle, selectedAngle:DrawAngle;
		private var magnet1:Shape;
		private var _angle1:DrawAngle = null;
		private var magnet1good:Boolean = false; // flag for drawing a (blue?) outline around an unset magnet2 indicating that it is ready to be set
		private var magnet1bad:Boolean = false; // flag for drawing a (blue?) outline around an unset magnet2 indicating that it is ready to be set
		private var marginOfErrorMessageGiven:Boolean=false;
		private var endpointsPinnedMessageGiven:Boolean=false;
		// for the "wiggle"
		private var initialError:Number; // the error, in degrees, between two "parallel" sides, before wiggle
		private var targetWiggle:Number; // how far the user needs to wiggle, by default min of two angles
		private var currentWiggle:Number; // how far the user has currently wiggled, in eighther direction
		private var previousWigglePoint:Point; // the previous point of this tool so we know how far we travelled in current wiggle
		
		override public function get outOfBounds():Boolean{return _outOfBounds;}
		public function get component1():DrawAngle{return this._angle1;}
		public function set component1(s:DrawAngle):void{this._angle1 = s;}
		public function get component2():DrawAngle{return null}
		public function set component2(s:DrawAngle):void{}
		override public function get components():Array
		{
			var arr:Array = new Array();
			if (_angle1 != null) arr.push(_angle1);
			return arr;
		}
		public function RightTool(drawAngles:Array, oobshapes:Array, marginOfError_degrees:Number,marginOfError_snap:Number=10)
		{
			super(this);
			this.drawAngles = drawAngles;
			this.oobshapes = oobshapes;
			this.mouseEnabled = false;
			this.mouseChildren = false;
			this.marginOfError_snap = marginOfError_snap;
			this.marginOfError_degrees = marginOfError_degrees;
			magnet1 = drawMagnet(180); // draw a new magnet
			this.addEventListener(Event.ADDED, handleAdded);
		}
		private function handleAdded(event:Event):void
		{
			this.removeEventListener(Event.ADDED, handleAdded);
			_state = "zero";
			drawCursor();
			// go through each point and add event listener
			for each (var a:DrawAngle in drawAngles)
			{
				a.mouseEnabled = true;
				if(!a.hasEventListener(MouseEvent.MOUSE_OVER)) a.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverAngle);
			}
			for each (var oobshape:OobShape in oobshapes)
			{
				oobshape.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverOobShape);				
			}
			this.addEventListener(Event.REMOVED, handleRemoved);
		}
		/** Mouse over should work for all segments to highlight except currently selected, unless allow disconnected */
		private function handleMouseOverAngle(event:MouseEvent):void
		{
			overAngle = DrawAngle(event.currentTarget);
			overAngle.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverAngle);
			overAngle.addEventListener(MouseEvent.MOUSE_OUT, handleMouseOutAngle);
			overAngle.addEventListener(MouseEvent.CLICK, handleClickAngle);
		}
		private function handleMouseOutAngle(event:MouseEvent):void
		{
			overAngle.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutAngle);
			overAngle.removeEventListener(MouseEvent.CLICK, handleClickAngle);
			overAngle.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverAngle);
			overAngle = null;
		}
		private function handleClickAngle (event:MouseEvent):void
		{
			if (selectedAngle != overAngle)
			{
				selectedAngle = overAngle;
				selectComponent(selectedAngle);
			} else
			{
				selectedAngle = null;
				clearComponent(selectedAngle);
			}			
		}
		// out of bounds objects
		private function handleMouseOverOobShape(event:MouseEvent):void
		{
			event.currentTarget.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverOobShape);
			_outOfBounds = true;
			event.currentTarget.addEventListener(MouseEvent.MOUSE_OUT, handleMouseOutOobShape);
		}
		private function handleMouseOutOobShape(event:MouseEvent):void
		{
			event.currentTarget.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutOobShape);
			_outOfBounds = false;
			event.currentTarget.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverOobShape);
		}
		override public function selectComponent(c:MovieClip):void
		{			
			var a:DrawAngle = DrawAngle(c);
			// select the first angle
			if (_angle1 == null)
			{
				_angle1 = a;
				// setup wiggle
				initialError = Math.abs(90 - _angle1.degrees);
				targetWiggle = 4*Math.min(_angle1.pointC.drawSegments[0].length, _angle1.pointC.drawSegments[1].length);
				currentWiggle = 0;
				previousWigglePoint = new Point(this.x, this.y);
			}  else
			{
				trace("only one may be selected");
			}
			
		}
		override public function clearComponent(c:MovieClip):void
		{
			var a:DrawAngle = DrawAngle(c);
			if (_angle1 != null && _angle1 == a)
			{
				_angle1 = null;
			}
		}
		
		override public function process(point:Point):void
		{
			var angle:DrawAngle = overAngle;
			
			// First angle not set
			if (_angle1 == null )
			{
				// do we have candidate angle?
				if (angle != null)
				{
					// have we updated the color of the magnet?
					if (!magnet1good && !magnet1bad)
					{
						if (Math.abs(angle.degrees - 90) <= marginOfError_degrees && !(angle.point1.pinned && angle.point2.pinned))
						{
							magnet1 = drawMagnet(1, magnet1);
							magnet1good = true; // so we don't change the graphics every frame
						} else
						{
							magnet1 = drawMagnet(-1, magnet1);
							if (!endpointsPinnedMessageGiven && (angle.point1.pinned && angle.point2.pinned))
							{
								GeometryGameItem(parent.parent).newMessage(this, "This angle can't be changed.", magnet1.x, magnet1.y, MessageBox.EPHEMERAL_TYPE);
								endpointsPinnedMessageGiven = true;
							} else if (!marginOfErrorMessageGiven &&  Math.abs(angle.degrees - 90) > marginOfError_degrees)
							{	
								GeometryGameItem(parent.parent).newMessage(this, "This angle isn't close enough to 90 degrees yet, try dragging first.", magnet1.x, magnet1.y, MessageBox.EPHEMERAL_TYPE);
								marginOfErrorMessageGiven = true;
							}
							
							magnet1bad = true; // so we don't change the graphics every frame
						}
					}
					this.x = point.x;
					this.y = point.y;
					this.rotation = angle.bisectorRotation;
				}// no candidate
				else
				{
					if (magnet1good || magnet1bad)
					{ // revert magnet to neutral
						magnet1 = drawMagnet(0, magnet1);
						magnet1good = false; // so we don't change the graphics every frame
						magnet1bad = false; // so we don't change the graphics every frame
					}
					this.x = point.x;
					this.y = point.y;
					this.rotation = 0;
				}					
			}// 1) Magnet1 set, looking for a second angle
			else if (_angle1 != null)
			{
				//this.x = angle2.midpoint.x;
				//this.y = point.y;
				
				// amount of wiggle from previous frame
				var thisWiggle:Number = MathUtilities.distance(this.x, this.y, previousWigglePoint.x, previousWigglePoint.y);
				// Should not exceed maxWiggleProp
				var wiggleProp:Number = thisWiggle/targetWiggle;//*initialError;
				if (wiggleProp < MAX_WIGGLE_PROP)
				{
					currentWiggle += thisWiggle;
				} else // in this case use .1 as maxWiggleProp and work backwards to get currentWiggle	
				{
					wiggleProp = MAX_WIGGLE_PROP;
					///currentWiggle += wiggleProp * targetWiggle / initialError;
					currentWiggle += wiggleProp * targetWiggle;
				}
				
				// have we reached target
				//trace(wiggleProp, thisWiggle, currentWiggle, targetWiggle);
				if (currentWiggle < targetWiggle)
				{
					// adjust angle
					//trace("dilate stuff", _angle2.length, "to", _angle1.length, wiggleProp);
					
					if(_angle1.adjustToRadians(Math.PI/2, currentWiggle/targetWiggle, null))
					{
						previousWigglePoint.x = this.x;
						previousWigglePoint.y = this.y;			
					} else
					{
						//this.dispatchEvent(new Event(Event.COMPLETE));
						GeometryGameItem(parent.parent).processCommand("right", "in_progress", this, components);
					}										
				} else
				{
					_angle1.adjustToRadians(Math.PI/2, 1, null);
					//this.dispatchEvent(new Event(Event.COMPLETE));
					GeometryGameItem(parent.parent).processCommand("right", "in_progress", this, components);
				}
				
				this.rotation = _angle1.bisectorRotation;
				this.y = point.y;
				this.x = point.x;
				point = new Point(this.x, this.y);
				
			}
		}
		private function drawCursor():void
		{
			switch (_state)
			{
				case "one":
					magnet1 = drawMagnet(180); // draw a new magnet
					break;
			}
		}
		/** Draws a half of the angle tool (two magnets connected)  
		 * State can be in three forms 1-good, 0-default, -1-bad */
		private function drawMagnet(state:int=0, magnet:Shape=null):Shape
		{
			var hdegrees:Number = Math.abs(90)/2;
			if (magnet == null)
			{
				magnet = new Shape();
				this.addChild(magnet);
			} else
			{
				magnet.graphics.clear();
			}
			// draw magnet at one end
			if (state == 1)
			{
				magnet.graphics.lineStyle(2, 0xAAAAFF);
			} else if (state == -1)
			{
				magnet.graphics.lineStyle(2, 0xFFAAAA);
			} else
			{
				magnet.graphics.lineStyle(2, 0xAAAAAA);
			}
			// "hand draw" a rectangle to utilize trig
			var rad:Number = MathUtilities.degToRad(hdegrees);
			var crad:Number = Math.PI/2 - rad;
			//magnet.graphics.drawRect(-MAGNET_WIDTH/2,-MAGNET_HEIGHT/2+armLength/2,MAGNET_WIDTH,MAGNET_HEIGHT);
			magnet.graphics.beginFill(0xCCCCCC);
			magnet.graphics.moveTo(-MAGNET_WIDTH/2*Math.sin(crad), MAGNET_WIDTH/2*Math.cos(crad));
			magnet.graphics.lineTo(-MAGNET_WIDTH/2*Math.sin(crad)-MAGNET_HEIGHT*Math.sin(rad), MAGNET_WIDTH/2*Math.cos(crad)-MAGNET_HEIGHT*Math.cos(rad));
			magnet.graphics.lineTo(MAGNET_WIDTH/2*Math.sin(crad)-MAGNET_HEIGHT*Math.sin(rad), -MAGNET_WIDTH/2*Math.cos(crad)-MAGNET_HEIGHT*Math.cos(rad));
			magnet.graphics.lineTo(MAGNET_WIDTH/2*Math.sin(crad), -MAGNET_WIDTH/2*Math.cos(crad));
			magnet.graphics.lineTo(-MAGNET_WIDTH/2*Math.sin(crad), MAGNET_WIDTH/2*Math.cos(crad));
			magnet.graphics.endFill();
			// other side
			rad = -rad;
			crad = -crad;
			magnet.graphics.beginFill(0xCCCCCC);
			magnet.graphics.moveTo(-MAGNET_WIDTH/2*Math.sin(crad), MAGNET_WIDTH/2*Math.cos(crad));
			magnet.graphics.lineTo(-MAGNET_WIDTH/2*Math.sin(crad)-MAGNET_HEIGHT*Math.sin(rad), MAGNET_WIDTH/2*Math.cos(crad)-MAGNET_HEIGHT*Math.cos(rad));
			magnet.graphics.lineTo(MAGNET_WIDTH/2*Math.sin(crad)-MAGNET_HEIGHT*Math.sin(rad), -MAGNET_WIDTH/2*Math.cos(crad)-MAGNET_HEIGHT*Math.cos(rad));
			magnet.graphics.lineTo(MAGNET_WIDTH/2*Math.sin(crad), -MAGNET_WIDTH/2*Math.cos(crad));
			magnet.graphics.lineTo(-MAGNET_WIDTH/2*Math.sin(crad), MAGNET_WIDTH/2*Math.cos(crad));
			magnet.graphics.endFill();
			
			// draw magnet on the other end
			// Draw rotation joint
			magnet.graphics.lineStyle(1,0xAAAAAA);
			magnet.graphics.beginFill(0xDDDDDD);
			magnet.graphics.drawEllipse(-JOINT_DIAMETER/2, -JOINT_DIAMETER/2, JOINT_DIAMETER, JOINT_DIAMETER);
			magnet.graphics.endFill();
			
			// draw "box"
			magnet.graphics.lineStyle(2,0x44FF44);
			magnet.graphics.moveTo(-MAGNET_WIDTH/2*Math.sin(crad)-MAGNET_HEIGHT/2*Math.sin(rad), MAGNET_WIDTH/2*Math.cos(crad)-MAGNET_HEIGHT/2*Math.cos(rad));
			magnet.graphics.lineTo(0, -MAGNET_HEIGHT/2*Math.sqrt(2));
			rad = -rad;
			crad = -crad;
			magnet.graphics.lineTo(-MAGNET_WIDTH/2*Math.sin(crad)-MAGNET_HEIGHT/2*Math.sin(rad), MAGNET_WIDTH/2*Math.cos(crad)-MAGNET_HEIGHT/2*Math.cos(rad));
			
			return magnet;
		}		
		/** When this is removed we need to remove stuff we placed on the parent object */
		private function handleRemoved(event:Event):void
		{
			trace("remove_angle");
			if (magnet1 != null)	
			{
				if (magnet1.parent == this.parent) this.parent.removeChild(magnet1);
			}
			this.removeEventListener(Event.REMOVED, handleRemoved);
			// go through each point and add event listener
			for each (var a:DrawAngle in drawAngles)
			{
				if(a.hasEventListener(MouseEvent.MOUSE_OVER)) a.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverAngle);
				if(a.hasEventListener(MouseEvent.MOUSE_OUT)) a.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutAngle);
				if(a.hasEventListener(MouseEvent.CLICK)) a.removeEventListener(MouseEvent.CLICK, handleClickAngle);
				a.mouseEnabled = false;
			}
			for each (var oobshape:OobShape in oobshapes)
			{
				if(oobshape.hasEventListener(MouseEvent.MOUSE_OVER)) oobshape.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverOobShape);				
				if(oobshape.hasEventListener(MouseEvent.MOUSE_OUT)) oobshape.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutOobShape);				
			}
		}
	}
}